import 'package:flutter/services.dart';

typedef ProgressCallBack = void Function(ObsBean bean);
typedef RefreshCallBack = void Function(List<ObsBean> fileBeans);
typedef FailureCallBack = void Function(ObsBean bean);
typedef SuccessCallBack = void Function(ObsBean bean);

class ObsBean {

  ObsResult status = ObsResult.unknown;

  bool? success;

  String endpoint = '';
  String bucketName = '';
  String accessKey = '';
  String accessSecret = '';
  String securityToken = '';
  String objectKey = '';
  String filePath = '';

  String fileName = '';
  String fileId = '';

  double progress = 0;

  int errorCode = 0;
  String errorMessage = '';
  String requestId = '';
  String error = '';

  ObsBean({String filePath = ''});
  ObsBean.fromObsJson(Map<dynamic, dynamic> json) {
    if(json['status'] != null) {
      status = ObsResult.values[int.parse(json['status'].toString())];
    }
    endpoint = json['endpoint'];
    fileId = json['fileId'];
    bucketName = json['bucketName'];
    accessKey = json['accessKey'];
    accessSecret = json['accessSecret'];
    securityToken = json['securityToken'];
    objectKey = json['objectKey'];
    filePath = json['filePath'];
    fileName = json['fileName'];
    if(json['progress'] != null) {
      progress = double.parse(json['progress'].toString());
    }
    if(json['errorCode'] != null) {
      errorCode = int.parse(json['errorCode'].toString());
    }
    errorMessage = json['errorMessage'];
    error = json['error'];
    requestId = json['requestId'];
  }

  Map<String, dynamic> toObsMap(){
    return {
      'endpoint': endpoint,
      'bucketName': bucketName,
      'accessKey': accessKey,
      'accessSecret': accessSecret,
      'objectKey': objectKey,
      'filePath': filePath,
      'fileName': fileName,
      'securityToken': securityToken,
      'fileId': fileId,
    };
  }
}

abstract class EventCallback {
  void onEvent(Map params);
}

enum ObsResult {
  unknown(""),// 未知不做任何操作
  upload("上传中"),// 上传中
  success("上传成功"),// 成功
  failure("上传失败");// 失败
  const ObsResult(this.text);
  final String text;
}

class FlutterObs with EventCallback {

  factory FlutterObs() => _instance;

  FlutterObs._internal() {
    NativeEvent().addCallBack(this);
  }

  static final FlutterObs _instance = FlutterObs._internal();

  /// 通道
  final MethodChannel _channel = const MethodChannel('com.obs.navigator');


  ProgressCallBack? _progressCallBack;
  FailureCallBack? _failureCallBack;
  SuccessCallBack? _successCallBack;
  RefreshCallBack? _refreshCallBack;
  /// 单次使用
  RefreshCallBack? _startRereshCallBack;
  ProgressCallBack? _startProgressCallBack;
  SuccessCallBack? _startSuccessCallBack;

  final Map<String, List<ObsBean>> _tasks = {};
  final Map<String, List<String>> _allTasks = {};

  // 开启证据上传
  startUpload(List<ObsBean> files, {ProgressCallBack? progressCallBack, SuccessCallBack? successCallBack, RefreshCallBack? refreshCallBack}) {
    String key = DateTime.now().microsecondsSinceEpoch.toString();
    _allTasks[key] = files.map((e) => e.objectKey).toList();
    _tasks[key] = [];
    _startRereshCallBack = refreshCallBack;
    _startProgressCallBack = progressCallBack;
    _startSuccessCallBack = successCallBack;

    return _channel.invokeMethod('startUpload', {'files': files.map((e) => e.toObsMap()).toList()});
  }

  @override
  void onEvent(Map params) {
    handleEvent(ObsBean.fromObsJson(params));
  }

  /// 处理obs回调
  void handleEvent(ObsBean bean) async {
    switch (bean.status) {
      case ObsResult.unknown:
        break;
      case ObsResult.upload:
        if(_progressCallBack != null) _progressCallBack!(bean);
        if(_startProgressCallBack != null) _startProgressCallBack!(bean);
        break;
      case ObsResult.success:
        bean.success = true;
        if(_successCallBack != null) _successCallBack!(bean);
        if(_startSuccessCallBack != null) _startSuccessCallBack!(bean);
        refreshCallBack(bean);
        break;
      case ObsResult.failure:
        bean.success = false;
        if (_failureCallBack != null) {
          _failureCallBack!(bean);
        }
        refreshCallBack(bean);
        break;
    }
  }

  void refreshCallBack(ObsBean bean){
    /// 记录成功的文件
    _allTasks.forEach((key, value) {
      for (var element in value) {
        if(element == bean.objectKey) {
          if(_tasks[key] != null) {
            _tasks[key]!.add(bean);
          }
        }
      }
    });
    /// 校验单次任务是否全部完成
    _allTasks.keys.toList().forEach((key) {
      if (_tasks[key] != null && _tasks[key]!.length == _allTasks[key]!.length) {
        if(_refreshCallBack != null) _refreshCallBack!(_tasks[key]!);
        if(_startRereshCallBack != null) _startRereshCallBack!(_tasks[key]!);
        _tasks.remove(key);
        _allTasks.remove(key);
      }
    });
  }

  /// 监听证据上传
  void addCallBack(
      {ProgressCallBack? progressCallBack,
        FailureCallBack? failureCallBack,
        SuccessCallBack? successCallBack,
        RefreshCallBack? refreshCallBack}) {
    _progressCallBack = progressCallBack;
    _failureCallBack = failureCallBack;
    _successCallBack = successCallBack;
    _refreshCallBack = refreshCallBack;
  }

  void removeCallBack() {
    _progressCallBack = null;
    _failureCallBack = null;
    _successCallBack = null;
    _refreshCallBack = null;
  }

}

class NativeEvent {
  factory NativeEvent() => _instance;
  static final NativeEvent _instance = NativeEvent._internal();

  final EventChannel _eventChannel = const EventChannel('com.obs/eventChannel');
  final List<EventCallback> _list = [];

  NativeEvent._internal() {
    final stream = _eventChannel.receiveBroadcastStream();
    stream.listen((event) {
      String? type = event["#@type@#"];
      if (type == null) {
        throw Exception("event channel lost key:#@type@#");
      }
      Map m = event;
      m.remove("#@type@#");
      for (var e in _list) {
        e.onEvent(m);
      }
    });
  }

  void addCallBack(EventCallback callback) {
    _list.add(callback);
  }

  void removeCallBack(EventCallback callback) {
    _list.remove(callback);
  }
}